/**
 * 模拟Koa中间件机制的函数
 * @param {Array} arr 中间件函数数组
 * @returns {Function} 返回一个执行所有中间件的函数
 * 当索引超出数组长度时，返回一个resolved的Promise
 * 这表示中间件链已经到达末尾
 * 这个Promise.resolve()不是用来"结束"函数，而是作为中间件链的终点标记
 * 当最后一个中间件调用next()时，会执行到这里，然后开始回溯执行
 * 执行当前中间件，并传入ctx和next函数
 * next函数会调用下一个中间件
 * 整个过程形成了洋葱模型：先顺序执行每个中间件的前半部分，
 * 到达最后一个中间件后，再逆序执行每个中间件的后半部分
 */
function A(fns) {
  return function () {
    const ctx = {};
    async function dispatch(index) {
      if (index >= fns.length) {
        return Promise.resolve();
      }
      return fns[index](ctx, ()=> dispatch(index+1))
    }
    dispatch(0);
  };
}

async function func1(ctx, next) {
  console.log('第一个中间件 开始')
  await next()
  console.log('第一个中间件 结束')
}

async function func2(ctx, next) {
  console.log('第二个中间件 开始')
  await next()
  console.log('第二个中间件 结束')
}

async function func3(ctx, next) {
  console.log('第三个中间件 开始')
  ctx.body = 'Hello World'
  console.log('第三个中间件 结束')
  // 第三个中间件没有 next，结束当前分支
}
// 执行中间件链
A([func1, func2, func3])();

/**
 * 执行流程说明：
 * 1. dispatch(0) 执行 func1
 * 2. func1中调用next()，实际上是调用dispatch(1)
 * 3. dispatch(1) 执行 func2
 * 4. func2中调用next()，实际上是调用dispatch(2)
 * 5. dispatch(2) 执行 func3
 * 6. func3执行完毕(没有调用next())，返回到func2的await next()之后
 * 7. func2继续执行剩余代码，然后返回到func1的await next()之后
 * 8. func1继续执行剩余代码，整个中间件链执行完毕
 * 
 * 如果func3也调用了next()，会执行dispatch(3)
 * 此时index >= arr.length，返回Promise.resolve()
 * 这个Promise.resolve()作为中间件链的终点，允许最后一个中间件的next()正常完成
 */